package config

import (
	"encoding/json"
	"errors"
	"io/fs"
	"os"
	"path/filepath"
)

type Settings struct {
	DefaultSaveDir   string `json:"default_save_dir"`
	MaxConcurrent    int    `json:"max_concurrent"`
	ChunkCount       int    `json:"chunk_count"`
	SpeedLimitBytes  int64  `json:"speed_limit_bytes"`
	ProxyURL         string `json:"proxy_url"`
	Language         string `json:"language"`
	MaxRetries       int    `json:"max_retries"`
	RetryBackoffSec  int    `json:"retry_backoff_sec"`
	AutoOpenFile     bool   `json:"auto_open_file"`
	AutoOpenFolder   bool   `json:"auto_open_folder"`
	AutoShutdown     bool   `json:"auto_shutdown"`
	ShutdownDelayMin int    `json:"shutdown_delay_min"`
	HttpAPIEnabled   bool   `json:"http_api_enabled"`
	HttpAPIPort      int    `json:"http_api_port"`
	HttpAPIToken     string `json:"http_api_token"`
	AutoCategorize   bool   `json:"auto_categorize"`
	// rule engine
	Rules        []Rule           `json:"rules,omitempty"`
	GroupLimits  map[string]int   `json:"group_limits,omitempty"`
	// BitTorrent options
	BTNoUpload       bool `json:"bt_no_upload"`
	BTListenPort     int  `json:"bt_listen_port"`
	BTEnableDHT      bool `json:"bt_enable_dht"`
	BTEnableUPnP     bool `json:"bt_enable_upnp"`
	BTDisablePEX     bool `json:"bt_disable_pex"`
	BTDisableTrackers bool `json:"bt_disable_trackers"`
	BTMaxPeersGlobal     int  `json:"bt_max_peers_global"`
	BTMaxPeersPerTorrent int  `json:"bt_max_peers_per_torrent"`
}

// Rule defines match conditions and actions applied to new tasks (first-match).
type Rule struct {
	Name       string           `json:"name"`
	Enabled    bool             `json:"enabled"`
	Cond       RuleConditions   `json:"cond"`
	Act        RuleActions      `json:"act"`
}

type RuleConditions struct {
	URLContains []string `json:"url_contains,omitempty"`
	ExtIn       []string `json:"ext_in,omitempty"`
	NameRegex   string   `json:"name_regex,omitempty"`
	CategoryIs  []string `json:"category_is,omitempty"`
}

type RuleActions struct {
	// SaveSubdir is appended to resolved save directory
	SaveSubdir string `json:"save_subdir,omitempty"`
	// Priority: High|Medium|Low
	Priority   string `json:"priority,omitempty"`
	// Optional per-task overrides
	Chunks            int    `json:"chunks,omitempty"`
	MaxRetries        int    `json:"max_retries,omitempty"`
	RetryBackoffSec   int    `json:"retry_backoff_sec,omitempty"`
	SpeedLimitMBps    int    `json:"speed_limit_mbps,omitempty"`
	ProxyURL          string `json:"proxy_url,omitempty"`
}

func defaults() *Settings {
	home, _ := os.UserHomeDir()
	def := filepath.Join(home, "Downloads")
	return &Settings{
		DefaultSaveDir:  def,
		MaxConcurrent:   3,
		ChunkCount:      4,
		SpeedLimitBytes: 0,
		ProxyURL:        "",
		Language:        "zh-CN",
		MaxRetries:      3,
		RetryBackoffSec: 5,
		AutoOpenFile:    false,
		AutoOpenFolder:  false,
		AutoShutdown:    false,
		ShutdownDelayMin: 1,
		HttpAPIEnabled:   true,
		HttpAPIPort:      17865,
		HttpAPIToken:     "",
		AutoCategorize:   true,
		Rules:            []Rule{},
		GroupLimits:      map[string]int{},
		BTNoUpload:       true,
		BTListenPort:     0,
		BTEnableDHT:      true,
		BTEnableUPnP:     true,
		BTDisablePEX:     false,
		BTDisableTrackers:false,
		BTMaxPeersGlobal:     0,
		BTMaxPeersPerTorrent: 0,
	}
}

func configPath() (string, error) {
	base, err := os.UserConfigDir()
	if err != nil || base == "" {
		// fallback to home dir
		home, _ := os.UserHomeDir()
		if home == "" { return "", errors.New("cannot resolve config dir") }
		base = filepath.Join(home, ".config")
	}
	dir := filepath.Join(base, "downloadmanager")
	return filepath.Join(dir, "config.json"), nil
}

// Load reads settings from disk if available; otherwise returns defaults.
func Load() *Settings {
	path, err := configPath()
	if err != nil { return defaults() }
	b, err := os.ReadFile(path)
	if err != nil {
		return defaults()
	}
	var cfg Settings
	if e := json.Unmarshal(b, &cfg); e != nil {
		return defaults()
	}
	// basic sane fallbacks
	if cfg.MaxConcurrent <= 0 { cfg.MaxConcurrent = 3 }
	if cfg.ChunkCount < 1 { cfg.ChunkCount = 4 }
	if cfg.MaxRetries < 0 { cfg.MaxRetries = 0 }
	if cfg.RetryBackoffSec <= 0 { cfg.RetryBackoffSec = 5 }
	if cfg.ShutdownDelayMin < 0 { cfg.ShutdownDelayMin = 0 }
	if cfg.HttpAPIPort <= 0 { cfg.HttpAPIPort = 17865 }
	// booleans default to false; enable categorization by default only if missing
	if !cfg.AutoCategorize {
		cfg.AutoCategorize = true
	}
	if cfg.BTListenPort < 0 { cfg.BTListenPort = 0 }
	// default enable DHT & UPnP if unspecified (zero value false)
	// keep user's explicit false as-is
	if !cfg.BTEnableDHT { cfg.BTEnableDHT = true }
	if !cfg.BTEnableUPnP { cfg.BTEnableUPnP = true }
	if cfg.BTMaxPeersGlobal < 0 { cfg.BTMaxPeersGlobal = 0 }
	if cfg.BTMaxPeersPerTorrent < 0 { cfg.BTMaxPeersPerTorrent = 0 }
	return &cfg
}

// Save writes settings to disk in JSON format.
func Save(s *Settings) error {
	path, err := configPath()
	if err != nil { return err }
	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return err }
	data, err := json.MarshalIndent(s, "", "  ")
	if err != nil { return err }
	return os.WriteFile(path, data, fs.FileMode(0o644))
}
